{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# পর্ব ২ঃ Syft Keras দিয়ে সুরক্ষিত মডেল সার্ভ করা (Secure Model Serving with Syft Keras)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "যেহেতু আপনার কাছে সাধারণ Keras দিয়ে ট্রেইন করা একটি মডেল রয়েছে, সেহেতু আপনি এখন তা দিয়ে প্রাইভেট প্রেডিকশান সার্ভ করতে পারবেন। Syft Keras দিয়ে আমরা তা করতে পারি।\n",
    "\n",
    "মডেলটি সুরক্ষিত ভাবে সার্ভ করার জন্য আমাদের তিনটি TFEWorkers (সার্ভার) প্রয়োজন। এর কারন হচ্ছে, TF Encrypted মূলত  [multi-party computation (MPC)](https://en.wikipedia.org/wiki/Secure_multi-party_computation) নামের একটি এনক্রিপশান প্রযুক্তির উপর ভিত্তি করে তৈরি। ধারণাটি হল মডেল weights এবং ইনপুট ডেটাগুলিকে ভাগ করে নেওয়া তারপরে প্রতিটি মানের একটি অংশ বিভিন্ন সার্ভারে প্রেরণ করা। এর মূল বৈশিষ্ট্যটি হল আপনি যদি যেকোনো একটি সার্ভারের ভাগের দিকে তাকান তবে এটি প্রকৃত মান (ইনপুট ডেটা বা মডেল weights) সম্পর্কে কিছুই প্রকাশ করে না।\n",
    "\n",
    "আমরা পূর্বের নোটবুকের মতো একটি Syft Keras মডেল সংজ্ঞায়িত করব। তবে, এখানে একটি কৌশল আছে: এই মডেলটি সংজ্ঞায়িত করার আগে, আমরা `hook = sy.KerasHook(tf.keras)` রান করব। এটি Keras Sequential ক্লাসে তিনটি গুরুত্বপূর্ণ নতুন পদ্ধতি যুক্ত করবে:\n",
    " - `share`: এটি সিক্রেট শেয়ারিং এর মাধ্যমে আপনার মডেলটিকে সুরক্ষিত করবে; সাধারণ অবস্থায় এটি TF Encrypted থেকে SecureNN প্রটোকল ব্যবহার করে তিনটি TFEWorkers এর মধ্যে আপনার মডেলটি গোপনে শেয়ার করবে। সবচেয়ে গুরুত্বপূর্ণ হল এটি এনক্রিপ্ট করা ডেটাতে প্রেডিকশান করার সুবিধা যোগ করবে। \n",
    " - `serve`: এই ফাংশানটি একটি সারভিং ক্যু (queue) চালু করবে, যাতে করে TFEWorker রা বহিরাগত ক্লায়েন্টদের থেকে সুরক্ষিত মডেলের উপর প্রেডিকশান রিকুয়েস্ট গ্রহণ করতে পারে। \n",
    " - `shutdown_workers`: একবার আপনার প্রাইভেট প্রেডিকশান সরবরাহ করা হয়ে গেলে, আপনি এই ফাংশনটি চালিয়ে আপনার মডেলটি বন্ধ করতে পারেন। আপনি যদি প্রতিটি Worker-কে ম্যানুয়ালি পরিচালনা করতে পছন্দ করে থাকেন তবে এটি আপনাকে ম্যানুয়ালি সার্ভার প্রক্রিয়াগুলি বন্ধ করার নির্দেশ দিবে।\n",
    "\n",
    "আপনি যদি MPC সম্মন্ধে আরও জানতে চান, তবে এই দারুন লেখাটি পড়তে পারেন [blog](https://mortendahl.github.io/2017/04/17/private-deep-learning-with-mpc/)।"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "অনুবাদক:\n",
    "\n",
    "- Sourav Das - Twitter: [@adventuroussrv](https://twitter.com/adventuroussrv)\n",
    "- Zarreen Reza - Twitter: [@zarreennreza](https://twitter.com/zarreennreza)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "from tensorflow.keras import Sequential\n",
    "from tensorflow.keras.layers import AveragePooling2D, Conv2D, Dense, Activation, Flatten, ReLU, Activation\n",
    "\n",
    "import syft as sy\n",
    "hook = sy.KerasHook(tf.keras)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## মডেল (Model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "আপনি দেখতে পাচ্ছেন, আমরা প্রায় একইভাবে মডেলটি সংজ্ঞায়িত করছি, শুধুমাত্র এবার আমরা batch_input_shape যোগ করেছি। এর ফলে TF Encrypted পূর্বনির্ধারিত টেনসর আকারগুলির সাহায্যে আরও ভালভাবে নিরাপদ গণনা করতে পারে। এই MNIST উদাহরনের ক্ষেত্রে, আমরা (১, ২৮, ২৮, ১) আকারে ইনপুট ডেটা প্রেরন করব।\n",
    "এছাড়াও আমরা softmax এর পরিবর্তে logit ফেরত পাঠাবো কেননা MPC এর ক্ষেত্রে softmax অপারেশানটি বেশ জটিল এবং প্রেডিকশান রিকুয়েস্ট সার্ভ করার জন্য এত জটিল অপারেশানের দরকার নেই।"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "num_classes = 10\n",
    "input_shape = (1, 28, 28, 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = Sequential()\n",
    "\n",
    "model.add(Conv2D(10, (3, 3), batch_input_shape=input_shape))\n",
    "model.add(AveragePooling2D((2, 2)))\n",
    "model.add(Activation('relu'))\n",
    "model.add(Conv2D(32, (3, 3)))\n",
    "model.add(AveragePooling2D((2, 2)))\n",
    "model.add(Activation('relu'))\n",
    "model.add(Conv2D(64, (3, 3)))\n",
    "model.add(AveragePooling2D((2, 2)))\n",
    "model.add(Activation('relu'))\n",
    "model.add(Flatten())\n",
    "model.add(Dense(num_classes, name=\"logit\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### পূর্বে ট্রেইন করা weights লোড করা (Load pre-trained weights)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "load_weights এর সাহায্যে আপনি সহজেই পূর্বে ট্রেইন করা সংরক্ষিত weight গুলো লোড করতে পারবেন।"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pre_trained_weights = 'short-conv-mnist.h5'\n",
    "model.load_weights(pre_trained_weights)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Worker গুলো চালু করুন (Launch the workers)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "চলুন এখন প্রাইভেট প্রেডিকশান সরবরাহ করার জন্য TF Encrypted এর জন্য প্রয়োজনীয় TFEWorker (alice, bob ও carol) তৈরি করি। প্রত্যেক TFEWorker এর জন্য আপনাকে শুধুমাত্র ওয়ার্কার এর হোস্ট নির্ধারণ করে দিতে হবে। এরপর আমরা এই ওয়ার্কারগুলোকে ক্লাস্টারে সমন্বিত করে দিব। \n",
    "\n",
    "এই ওয়ার্কারগুলো [TensorFlow server](https://www.tensorflow.org/api_docs/python/tf/distribute/Server) এ রান করে, যেটা আপনি হয় নিজেই ম্যানাজ করতে পারবেন (`AUTO = False`) অথবা ওয়ার্কার গুলো আপনার জন্য তা করে দিবে (`AUTO = True`)। যদি আপনি নিজে ম্যানাজ করার অপশানটি নির্বাচন করেন, তাহলে নিচে cluster.start() রান করার পর আপনার কাছে প্রতিটি ওয়ার্কারের হোস্ট ডিভাইসে একটি টার্মিনাল কমান্ড রান করার নির্দেশিকা আসবে। যদি প্রতিটা ওয়ার্কার একটিমাত্র ডিভাইসে (যেমন localhost) হোস্ট করা হয়ে থাকে, তবে আপনি চাইলে Syft  স্বয়ংক্রিয়ভাবে প্রতিটি ওয়ার্কারের TensorFlow সার্ভার ম্যানাজ করে দিবে। "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "AUTO = False\n",
    "\n",
    "alice = sy.TFEWorker(host='localhost:4000', auto_managed=AUTO)\n",
    "bob = sy.TFEWorker(host='localhost:4001', auto_managed=AUTO)\n",
    "carol = sy.TFEWorker(host='localhost:4002', auto_managed=AUTO)\n",
    "\n",
    "cluster = sy.TFECluster(alice, bob, carol)\n",
    "cluster.start()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Weights শেয়ার করে মডেলটি সুরক্ষিত করুন  (Secure the model by sharing the weights)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "sy.KerasHook(tf.keras) কে ধন্যবাদ যে আপনি share মেথড কল করে আপনার মডেলকে TF Encrypted Keras মডেলে রূপান্তরিত করতে পারছেন। \n",
    "আপনি যদি উপরে সার্ভারগুলি ম্যানুয়ালি পরিচালনা করতে বলে থাকেন তবে সমস্ত পদক্ষেপ না নেওয়া পর্যন্ত এই ধাপটি সম্পূর্ণ হবে না। মনে রাখবেন যে আপনার ফায়ারওয়াল পাইথনকে (Python) আগত সংযোগ গ্রহণ করতে চাইতে পারে।"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.share(cluster)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### মডেল সার্ভ করুন (Serve model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "অসাধারণ! এখন model.serve কল করার মাধ্যমে আপনার মডেল প্রাইভেট প্রেডিকশান প্রদান করতে পারবে। আপনি num_requests এর মান নির্ধারণের মাধ্যমে আপনার মডেলের প্রেডিকশান রিকুয়েস্ট এর সংখ্যা নিয়ন্ত্রন করতে পারবেন, অন্যথায় বাধাপ্রাপ্ত না হওয়া পর্যন্ত মডেল প্রেডিকশান রিকুয়েস্ট সার্ভ করে যাবে। "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.serve(num_requests=3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "আপনি এখন পর্ব-১৩সি এর নোটবুক ব্যবহার করে প্রাইভেট প্রেডিকশান রিকুয়েস্ট করতে প্রস্তুত। "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### পরিষ্করণ (Cleanup!)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "একবার আপনার মডেলের রিকুয়েস্টের সীমা অতিক্রম হয়ে গেলে তা আর রিকুয়েস্ট সার্ভ করতে পারবেনা, তবে তারপরও গোপনীয়তার সাথে তিনটি ওয়ার্কারের মাঝে মডেলটি শেয়ার হতে থাকবে। নিচের সেলটি রান করে আপনি ওয়ার্কার গুলো বন্ধ করতে পারবেন। \n",
    "\n",
    "**পর্ব-১৩বিঃ Syft Keras ও TFE দিয়ে নিরাপদ শ্রেণীকরণ শেষ করায় আপনাকে অভিনন্দন! 
"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.stop()\n",
    "cluster.stop()\n",
    "\n",
    "if not AUTO:\n",
    "    process_ids = !ps aux | grep '[p]ython -m tf_encrypted.player --config' | awk '{print $2}'\n",
    "    for process_id in process_ids:\n",
    "        !kill {process_id}\n",
    "        print(\"Process ID {id} has been killed.\".format(id=process_id))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.17"
  },
  "nbTranslate": {
   "displayLangs": [
    "*"
   ],
   "hotkey": "alt-t",
   "langInMainMenu": true,
   "sourceLang": "en",
   "targetLang": "fr",
   "useGoogleTranslate": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
